package com.uptang.paper.main;

import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.xwpf.usermodel.IBodyElement;
import org.apache.poi.xwpf.usermodel.UnderlinePatterns;
import org.apache.poi.xwpf.usermodel.VerticalAlign;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFPicture;
import org.apache.poi.xwpf.usermodel.XWPFPictureData;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture;
import org.openxmlformats.schemas.officeDocument.x2006.math.CTOMath;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author cht
 * @version 4.2.2
 * @date 2020-12-23
 */
@Slf4j
@RestController
@RequestMapping("/paper")
public class PaperThreeController {

    @RequestMapping(value = "/import", method = RequestMethod.POST, produces = "application/json")
    public String paperDownload(@RequestParam("word") MultipartFile file) {
        StringBuilder text = new StringBuilder();
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
            XWPFDocument xDocument = new XWPFDocument(inputStream);
            List<XWPFParagraph> paragraphs = xDocument.getParagraphs();
            List<XWPFPictureData> pictures = xDocument.getAllPictures();
            List<XWPFTable> tables = xDocument.getTables();
            List<IBodyElement> bodyElements = xDocument.getBodyElements();
            // TODO 读取表格 Table
            StringBuilder content = new StringBuilder();

            // 图片处理,暂未使用
            Map<String, String> map = Maps.newHashMap();
            for (XWPFPictureData picture : pictures) {
                String id = picture.getParent().getRelationId(picture);
                String rawName = picture.getFileName();
                byte[] data = picture.getData();
                // TODO 上传图片到SSO
                String cloudSrc = "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3808727740,1583897060&fm=26&gp=0.jpg";
                map.put(id, cloudSrc);
            }

            ImageParse imageParser = new ImageParse();

            for (IBodyElement element : bodyElements) {
                if (element instanceof XWPFParagraph) {
                    handlerParagraph(content, imageParser, (XWPFParagraph) element);
                } else if (element instanceof XWPFTable) {
                    handlerTable(content, (XWPFTable) element);
                }
            }

            log.info("content:{}", content.toString());
            createFile(content.toString());
        } catch (Exception e) {
            log.error("WORD导入获取流异常:" + e.getMessage());
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error("WORD导入关闭流异常:" + e.getMessage());
                }
            }
        }
        return text.toString();
    }

    private void createFile(String data) {
        File file = new File("F:\\image\\index.html");


        // if file doesnt exists, then create it
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        FileWriter fw = null;
        try {
            fw = new FileWriter(file.getAbsoluteFile());
        } catch (IOException e) {
            e.printStackTrace();
        }
        BufferedWriter bw = new BufferedWriter(fw);
        try {
            bw.write(data);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            bw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }


    private void handlerTable(StringBuilder content, XWPFTable table) throws IOException {
        ReadWordTable readWordTable = new ReadWordTable();
        content.append(readWordTable.readTable(table));
    }

    private void handlerParagraph(StringBuilder content, ImageParse imageParser, XWPFParagraph paragraph) {
        ParagraphChildOrderManager runOrMaths = new ParagraphChildOrderManager(paragraph);
        List<Object> childList = runOrMaths.getChildList();
        for (Object child : childList) {
            if (child instanceof XWPFRun) {
                handleParagraphRun(content, (XWPFRun) child, imageParser);
            } else if (child instanceof CTOMath) {
                handleParagraphOMath(content, (CTOMath) child, imageParser);
            }
        }
    }

    private void handleParagraphOMath(StringBuilder content, CTOMath child, ImageParse imageParser) {
        String s = OmmlUtils.convertOmathToPng(child, imageParser);
        content.append(s);
    }

    private void handleParagraphRun(StringBuilder content, XWPFRun run, ImageParse imageParser) {
        // 有内嵌的图片
        List<XWPFPicture> pics = run.getEmbeddedPictures();
        if (pics != null && pics.size() > 0) {
            handleParagraphRunsImage(content, pics, imageParser);
        } else if (isMath(run)) {
            // 处理wps的公式图片
            handleParagraphRunsImageMath(content, run, imageParser);
        } else { //处理文本
            handleParagraphRunsWithText(content, run);
        }
    }

    private void handleParagraphRunsWithText(StringBuilder content, XWPFRun run) {
        String c = run.toString();
        c = escapeHtmlTag(c);

        if (c.length() == 0 && run.getCTR().getSymList().size() == 0) {
            return;
        }
        if (run.getSubscript() != null) {
            VerticalAlign va = run.getSubscript();
            if (va.equals(VerticalAlign.SUBSCRIPT)) {
                c = "<sub>" + c + "</sub>";
            } else if (va.equals(VerticalAlign.SUPERSCRIPT)) {
                c = "<sup>" + c + "</sup>";
            }
        }
        if (run.isBold()) {
            c = "<b>" + c + "</b>";
        } else if (run.isItalic()) {
            c = "<i>" + c + "</i>";
        } else if (run.isStrikeThrough()) {
            c = "<strike>" + c + "</strike>";
        }
        if (run.getUnderline() != null && run.getUnderline() != UnderlinePatterns.NONE) {
            c = "<span style='text-decoration: underline;'>" + c + "</span>";
        }
        if (run.getColor() != null) {
            c = "<span style='color:#" + run.getColor().toLowerCase() + ";'>" + c + "</span>";
        }
        if (run.getCTR().getSymList().size() > 0) {

            Node runNode = run.getCTR().getDomNode();
            Node objectNode = getChildNode(runNode, "w:sym");
            if(Objects.isNull(objectNode)) {
                return;
            }
            // 获取char值,该值为symbol的十六进制值
            NamedNodeMap attributes = objectNode.getAttributes();
            String fontFamily = null;
            if (attributes != null && attributes.getNamedItem("w:char") != null) {
                String value = attributes.getNamedItem("w:char").getNodeValue();
                fontFamily = attributes.getNamedItem("w:font").getNodeValue();
                String s = unicodeToString("\\u" + value);
                c = "<span style=font-family:" + fontFamily + ">" + s + "</span>";
            }

        }
        content.append(c);
    }

    public static String unicodeToString(String unicode) {
        StringBuffer sb = new StringBuffer();
        String[] hex = unicode.split("\\\\u");
        for (int i = 1; i < hex.length; i++) {
            int index = Integer.parseInt(hex[i], 16);
            sb.append((char) index);
        }
        return sb.toString();
    }



    private String escapeHtmlTag(String text) {
        text = text.replace("&", "&amp;");
        text = text.replace("<", "&lt;");
        text = text.replace(">", "&gt;");
        return text;
    }

    // 处理图片
    private void handleParagraphRunsImage(StringBuilder content, List<XWPFPicture> pics, ImageParse imageParser) {
        for (XWPFPicture pic : pics) {
            String desc = pic.getDescription();

            String path = imageParser.parse(pic.getPictureData().getData(),
                    pic.getPictureData().getFileName());
            log.info("pic.getPictureData().getFileName()===" + pic.getPictureData().getFileName());

            CTPicture ctPicture = pic.getCTPicture();
            Node domNode = ctPicture.getDomNode();

            Node extNode = getChildChainNode(domNode, "pic:spPr", "a:ext");
            NamedNodeMap attributes = extNode.getAttributes();
            if (attributes != null && attributes.getNamedItem("cx") != null) {
                int width = WordMyUnits.emuToPx(new Double(attributes.getNamedItem("cx").getNodeValue()));
                int height = WordMyUnits.emuToPx(new Double(attributes.getNamedItem("cy").getNodeValue()));
                content.append(String.format("<img src=\"%s\" width=\"%d\" height=\"%d\" />", path, width, height));
            } else {
                content.append(String.format("<img src=\"%s\" />", path));
            }
        }
    }

    public Node getChildChainNode(Node node, String... nodeName) {
        Node childNode = node;
        for (int i = 0; i < nodeName.length; i++) {
            String tmp = nodeName[i];
            childNode = getChildNode(childNode, tmp);
            if (childNode == null) {
                return null;
            }
        }
        return childNode;
    }

    // 处理wps的内嵌的图片公式，里面有wmf to svg的工具类
    private boolean isMath(XWPFRun run) {
        Node runNode = run.getCTR().getDomNode();
        Node objectNode = getChildNode(runNode, "w:object");
        if (objectNode == null) {
            return false;
        }
        Node shapeNode = getChildNode(objectNode, "v:shape");
        if (shapeNode == null) {
            return false;
        }
        Node imageNode = getChildNode(shapeNode, "v:imagedata");
        if (imageNode == null) {
            return false;
        }
        Node binNode = getChildNode(objectNode, "o:OLEObject");
        if (binNode == null) {
            return false;
        }
        return true;
    }


    private void handleParagraphRunsImageMath(StringBuilder content, XWPFRun run, ImageParse imageParser) {
        Node runNode = run.getCTR().getDomNode();
        XWPFDocument runDocument = run.getDocument();
        Node objectNode = getChildNode(runNode, "w:object");
        if (objectNode == null) {
            return;
        }
        Node shapeNode = getChildNode(objectNode, "v:shape");
        if (shapeNode == null) {
            return;
        }
        Node imageNode = getChildNode(shapeNode, "v:imagedata");
        if (imageNode == null) {
            return;
        }
        Node binNode = getChildNode(objectNode, "o:OLEObject");
        if (binNode == null) {
            return;
        }
        NamedNodeMap shapeAttrs = shapeNode.getAttributes();
        // 图片在Word中显示的宽高
        String style = shapeAttrs.getNamedItem("style").getNodeValue();

        NamedNodeMap imageAttrs = imageNode.getAttributes();
        // 图片在Word中的ID
        String imageRid = imageAttrs.getNamedItem("r:id").getNodeValue();
        // 获取图片信息
        PackagePart imgPart = runDocument.getPartById(imageRid);
        // word/media/image4.wmf
        String fullName = imgPart.getPartName().getName();
        log.info("pic.Match.fullName==" + fullName);
        String extName = imgPart.getPartName().getExtension();
        Pattern p = Pattern.compile("\\w+\\." + extName);
        Matcher matcher = p.matcher(fullName);
        if (matcher.find()) {
            fullName = matcher.group(0);
        }
        log.info("pic.Match.name==" + fullName);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            WmfUtils.toSvg(imgPart.getInputStream(), out);
        } catch (IOException e) {
            e.printStackTrace();
        }
        fullName = fullName.replace(extName, "svg");
        String path = imageParser.parse(new ByteArrayInputStream(out.toByteArray()), fullName);
        content.append("<img src=\"" + path + "\" style=\"" + style + "\"/>");
    }

    /**
     * 获取字符串
     *
     * @param runNode
     * @return
     */
    private String getText(Node runNode) {
        Node textNode = getChildNode(runNode, "w:t");
        if (textNode == null) {
            return "";
        }
        return textNode.getFirstChild().getNodeValue();
    }

    private String getMath(XWPFRun run, Node runNode) throws Exception {
        Node objectNode = getChildNode(runNode, "w:object");
        if (objectNode == null) {
            return "";
        }
        Node shapeNode = getChildNode(objectNode, "v:shape");
        if (shapeNode == null) {
            return "";
        }
        Node imageNode = getChildNode(shapeNode, "v:imagedata");
        if (imageNode == null) {
            return "";
        }
        Node binNode = getChildNode(objectNode, "o:OLEObject");
        if (binNode == null) {
            return "";
        }

        XWPFDocument word = run.getDocument();

        NamedNodeMap shapeAttrs = shapeNode.getAttributes();
        // 图片在Word中显示的宽高
        String style = shapeAttrs.getNamedItem("style").getNodeValue();
        System.out.println("图片宽高：".concat(style));

        System.out.println("--------------");

        NamedNodeMap imageAttrs = imageNode.getAttributes();
        // 图片在Word中的ID
        String imageRid = imageAttrs.getNamedItem("r:id").getNodeValue();
        // 获取图片信息
        PackagePart imgPart = word.getPartById(imageRid);
        System.out.println("图片名称".concat(imgPart.getPartName().getName()));
        System.out.println(imgPart.getInputStream());

        System.out.println("--------------");

        NamedNodeMap binAttrs = binNode.getAttributes();
        // 公式二进制文件在Word中的ID
        String binRid = binAttrs.getNamedItem("r:id").getNodeValue();
        // 获取二进制文件
        PackagePart binPart = word.getPartById(binRid);
        System.out.println("二进制文件名称：".concat(binPart.getPartName().getName()));
        System.out.println(binPart.getInputStream());

        System.out.println("--------------");

        return "{公式#}";
    }

    private Node getChildNode(Node node, String nodeName) {
        if (!node.hasChildNodes()) {
            return null;
        }
        NodeList childNodes = node.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node childNode = childNodes.item(i);
            if (nodeName.equals(childNode.getNodeName())) {
                return childNode;
            }
            childNode = getChildNode(childNode, nodeName);
            if (childNode != null) {
                return childNode;
            }
        }
        return null;
    }

}
